import json
import asyncio
import aio_pika
import aiormq
import logging
from aiohttp import web
# from bson import json_util


logger = logging.getLogger(__name__)


async def setup_rabbitmq(app: web.Application) -> None:
    """
    Open connection to RabbitMQ and create listener task, 
    try to reconnect every 10 seconds if there is a problem.
    """
    # config = app["config"]
    loop = asyncio.get_event_loop()
    try:
        connection: aio_pika.Connection = await aio_pika.connect_robust(host='rmq', loop=loop)
    except (ConnectionError, aiormq.exceptions.IncompatibleProtocolError) as e:
        logger.error(f"action=setup_rabbitmq, status=fail, retry=10s, {e}")
        await asyncio.sleep(10)
        await setup_rabbitmq(app)
        return None

    app["rabbitmq"] = connection
    app["rabbitmq_listener"] = asyncio.create_task(listen_events(app))

    logger.info(f"action=setup_rabbitmq, status=success")


async def close_rabbitmq(app: web.Application) -> None:
    if app.get("rabbitmq_listener"):
        app["rabbitmq_listener"].cancel()
        await app["rabbitmq_listener"]
    if app.get("rabbitmq"):
        await app["rabbitmq"].close()
    logger.info("action=close_rabbitmq, status=success")


async def on_message(app, message: aio_pika.IncomingMessage) -> None:
    """ Choose a handler for message processing by routing key. """
    # handlers = {"auth.event.authenticated": services.update_user_online_time}
    async with message.process():
        # handler = handlers[message.routing_key]
        # body = json_util.loads(message.body, json_options=json_util.RELAXED_JSON_OPTIONS)
        # await handler(**body)
        print(f" [x] Recieved {message.body}")
        upd = message.body.decode("utf-8")
        await app.store.bots_worker.handle_update(json.loads(upd))
        # await self.store.bots_worker.handle_update(body)



async def listen_events(app: web.Application) -> None:
    """ Declare queue, message binding and start the listener. """
    connection = app["rabbitmq"]
    try:
        channel = await connection.channel()
        await channel.set_qos(prefetch_count=100)

        auth_exchange = await channel.declare_exchange(name="hello", type=aio_pika.ExchangeType.TOPIC, durable=True)

        queue = await channel.declare_queue(name="hello", durable=True)
        await queue.bind(auth_exchange, routing_key="hello")

        async with queue.iterator() as queue_iter:
            async for message in queue_iter:
                await on_message(app, message)
    except asyncio.CancelledError:
        pass